# 导入 Python 的 socket 模块用于创建和管理网络连接
import socket
# 导入  threading  模块 用于实现多线程方便同时处理多个客户端连接
import threading
# 导入  time  模块用于在客户端中实现重试间隔
import time

# 服务端代码
# start_server 函数：定义服务端的启动逻辑
def start_server():
    # host ：服务端绑定的 IP 地址 这里是本地网络中的一个 IP
    host = "0.0.0.0"  # 手机本地IP
    # port ：服务端监听的端口号 客户端需要连接到这个端口
    port = 0            # 端口自定义
    
    # 创建一个 TCP 套接字（socket） AF_INET  表示使用 IPv4 地址 SOCK_STREAM  表示使用 TCP 协议
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 将套接字绑定到指定的 IP 地址和端口
    server_socket.bind((host, port))
    # 开始监听连接请求 5 是最大连接队列长度 表示最多可以有 5 个未处理的连接请求
    server_socket.listen(5)
    # 打印服务器正在监听的地址和端口 方便调试
    print(f"QPython 服务器正在监听 {host}:{port}")

    # 阻塞等待客户端的连接请求 当有客户端连接时 返回一个连接对象  connection  和客户端的地址  address 
    while True:
        connection, address = server_socket.accept()
        # 打印客户端的地址 表示有新的客户端连接
        print(f"来自 {address} 的连接")
        
        # 接收客户端发送的消息  1024  是缓冲区大小（单位是字节）表示最多接收 1024 字节的数据
        # decode() ：将接收到的字节数据解码为字符串
        while True:
            message = connection.recv(1024).decode()
            # 如果客户端发送的消息为空（即客户端断开连接）或者消息内容为  'exit'  则退出当前循环 关闭与该客户端的连接
            if not message or message.lower() == 'exit':
                # 打印提示信息 告知服务器端某个客户端已断开连接
                print(f"客户端断开连接: {address}")
                # 退出当前客户端的通信循环 停止与该客户端的进一步交互
                break
            # 打印客户端发送的消息
            print(f"收到来自 {address} 的消息: {message}")
            # 构造一个响应消息  "服务器已收到消息"  并将其编码为字节数据后发送给客户端
            response = "服务器已收到消息"
            connection.send(response.encode())
        # 关闭与当前客户端的连接
        connection.close()

# 客户端代码
# start_client  函数：定义客户端的启动逻辑
def start_client():
    # host ：服务器的 IP 地址 客户端需要连接到这个地址
    host = "0.0.0.0"  # 替换为 Python 服务器的 IP 地址
    # port ：服务器的端口号 客户端需要连接到这个端口
    port = 0            # 替换为 python 服务器的端口
    # retry_interval ：如果连接失败 客户端会等待这个时间后再重试
    retry_interval = 5  # 重试间隔时间（秒）

    # 使用  while True  循环 确保客户端不断尝试连接服务器
    while True:
         # 使用  try  块来捕获可能出现的异常 例如连接失败
         try:
            # 创建一个 TCP 套接字 用于客户端与服务器通信 
            client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            # 尝试连接到服务器的指定 IP 和端口
            client_socket.connect((host, port))
            # 如果连接成功 打印成功信息
            print("已连接到 Python 服务器 可以开始发送消息")
            # 打印提示信息 告知用户输入 'exit' 可以退出程序
            print("输入 'exit' 退出")
            # 打印提示信息 告知用户如果没有消息要发送 可以直接按回车键
            print("如果没有发送消息就任意输入回车就好")
            # # 退出当前的连接尝试循环 进入消息发送循环
            break
        # 捕获连接被拒绝的异常   
        except ConnectionRefusedError:
            # 打印提示信息 告知用户连接被拒绝 并说明将在多少秒后重试
            print(f"连接到 {host}:{port} 被拒绝{retry_interval} 秒后重试...")
            # 等待指定的重试间隔时间
            time.sleep(retry_interval)
            
    # 提示用户输入消息 input 函数会阻塞等待用户输入
    while True:
        message = input("发送消息: ")
        # 检查用户输入的消息是否为 'exit'（不区分大小写）
        if message.lower() == 'exit':
            # 如果用户输入 'exit' 将该消息发送给服务器
            client_socket.send(message.encode())
            # 发送完 'exit' 消息后 退出当前的客户端消息发送循环
            break
        # 将用户输入的消息编码为字节数据后发送给服务器
        client_socket.send(message.encode())
        # 接收服务器的响应消息 recv(1024)  表示最多接收 1024 字节的数据 然后解码为字符串
        response = client_socket.recv(1024).decode()
        # 打印服务器的响应消息
        print("服务器响应:", response)
    
    # 关闭客户端的套接字释放网络资源
    client_socket.close()
    # # 打印提示信息 告知用户客户端已成功关闭连接
    print("连接已关闭")

# 主程序
if __name__ == "__main__":
    # 创建服务器线程
    server_thread = threading.Thread(target=start_server)
    # # 设置为守护线程 以便主程序退出时关闭服务器线程
    server_thread.daemon = True
    # # 启动服务器线程 开始运行服务端的逻辑
    server_thread.start()

    # 等待服务器启动
    time.sleep(1)

    # 启动客户端
    start_client()
